﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using DetectingKinectSencor._40_Tools;

namespace DetectingKinectSencor
{
    /// <summary>
    /// The following class was provided by the book 
    /// Microsoft.Press.Programming.with.the.Kinect.for.Windows.Software.Development.Kit.Sep.2012
    /// PlayerSystem<T> where T : FramePlayer
    /// -> This means that there are specific constraints.
    /// Compiler must have some guarantee that the operator or method it needs to call will be 
    /// supported by any type argument that might be specified by client code.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class PlayerSystem<T> where T : FramePlayer, new()
    {
        internal event Action<T> FrameReady;
        public List<T> frames = new List<T>();

        CancellationTokenSource cancellationTokenSource;
                
        private static DateTime startpoint;
        private TimeSpan differenceStartToCurrentColor = new TimeSpan();
        private TimeSpan differenceStartToCurrentSkeleton = new TimeSpan();
        private TimeSpan differenceStartToCurrentDepth = new TimeSpan();
        private long totalMillisecondsColor;
        private long totalMillisecondsSkeleton;
        private DateTime startpointWithMillisecondsColor;
        private DateTime startpointWithMillisecondsSkeleton;
        private static bool startPointAlreadySet;
        private bool endPointAlreadySet;
        private DateTime tempStartPoint;
        private long totalMillisecondsDepth;
        private DateTime startpointWithMillisecondsDepth;


        //Define getter and setter method for is finished
        public bool IsFinished
        {
            get;
            private set;
        }

        /// <summary>
        /// Adds the frame of the BinaryReader to the frames variable
        /// </summary>
        /// <param name="reader"></param>
        internal void AddFrame(BinaryReader reader)
        {
            //Create a new object form type T, T has all the constraints,variables, methods of FramePlayer
            T frame = new T();

            // Pass the reader to the CreateFromReader method, the method is overwritten in; PlayColorStream, 
            // PlayDepthStream, PlaySkeletronStream,
            frame.CreateFromReader(reader);

            //Add the frame object to the list of frames
            frames.Add(frame);
        }


        /// <summary>
        /// Start playing the saved stream
        /// </summary>
        public void Start()
        {
            // If canellationTokensource is set, than it is canceled
            Stop();

            // Set the IsFinshed variable to false,
            IsFinished = false;


            cancellationTokenSource = new CancellationTokenSource();
            CancellationToken token = cancellationTokenSource.Token;


            if (!startPointAlreadySet)
            {
                startpoint = DateTime.Now;
                tempStartPoint = DateTime.Now;
                startPointAlreadySet = true;

            }

            //Labmda expression is used, on the left are the input parameters and on the right the expression
            Task.Factory.StartNew(() =>
            {


                foreach (T frame in frames)
                {

                    // In this if, the additional delay is calculated most of the time the color stream is too slow,
                    // hence the sekeleton stream needs to be adapted


                    if (frame.type.Equals("color"))
                    {
                        totalMillisecondsColor += frame.timeStamp;

                        // defines at which time the frame should be shown
                        startpointWithMillisecondsColor = startpoint.AddMilliseconds(totalMillisecondsColor);

                        //difference when the frame is actually shown
                        differenceStartToCurrentColor = startpointWithMillisecondsColor - DateTime.Now;


                        // if negative than it is too slow, if it is positiv than it is too fast an needs to sleep
                        if (differenceStartToCurrentColor.Milliseconds >= 0)
                        {
                            // Let the system sleep for a given time, after which the task raises
                            // an event for the KinectReader to signal that a new frame is available
                            Thread.Sleep(TimeSpan.FromMilliseconds(differenceStartToCurrentColor.Milliseconds));
                        }
                        else
                        {
                            startpoint = startpoint.Add(-differenceStartToCurrentColor);
                        }

                    }


                    if (frame.type.Equals("skeleton"))
                    {
                        totalMillisecondsSkeleton += frame.timeStamp;

                        startpointWithMillisecondsSkeleton = startpoint.AddMilliseconds(totalMillisecondsSkeleton);

                        //difference when the frame is actually shown
                        differenceStartToCurrentSkeleton = startpointWithMillisecondsSkeleton - DateTime.Now;

                        // if negative than it is too slow, if it is positiv than it is too fast an needs to sleep
                        if (differenceStartToCurrentSkeleton.Milliseconds >= 0)
                        {
                            // Let the system sleep for a given time, after which the task raises
                            // an event for the KinectReader to signal that a new frame is available
                            Thread.Sleep(TimeSpan.FromMilliseconds(differenceStartToCurrentSkeleton.Milliseconds));
                        }
                        else
                        {
                            startpoint = startpoint.Add(-differenceStartToCurrentSkeleton);
                        }
                    }


                    if (frame.type.Equals("depth"))
                    {
                        totalMillisecondsDepth += frame.timeStamp;

                        startpointWithMillisecondsDepth = startpoint.AddMilliseconds(totalMillisecondsDepth);

                        //difference when the frame is actually shown
                        differenceStartToCurrentDepth = startpointWithMillisecondsDepth - DateTime.Now;

                        // if negative than it is too slow, if it is positiv than it is too fast an needs to sleep
                        if (differenceStartToCurrentDepth.Milliseconds >= 0)
                        {
                            // Let the system sleep for a given time, after which the task raises
                            // an event for the KinectReader to signal that a new frame is available
                            Thread.Sleep(TimeSpan.FromMilliseconds(differenceStartToCurrentDepth.Milliseconds));
                        }
                        else
                        {
                            startpoint = startpoint.Add(-differenceStartToCurrentDepth);
                        }
                    }


                    // A cancel request is that so the loop is exit
                    if (token.IsCancellationRequested)
                        break;

                    // The event is fired an the frame is transferred
                    if (FrameReady != null)
                        FrameReady(frame);
                }


                totalMillisecondsColor = 0;
                totalMillisecondsSkeleton = 0;
                startPointAlreadySet = false;


                if (!endPointAlreadySet)
                {
                    Console.WriteLine("1 length: " + DateTime.Now.Subtract(startpoint));
                    endPointAlreadySet = true;
                }

                //IsFinshed variable is set to true
                IsFinished = true;
            }, token);



        }

        /// <summary>
        /// Method to stop the play
        /// </summary>
        public void Stop()
        {
            if (cancellationTokenSource == null)
                return;
            cancellationTokenSource.Cancel();

        }
           

    }

}